package com.ews.onlineexchange.model;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.JoinTable;
import javax.persistence.Lob;
import javax.persistence.ManyToOne;
import javax.persistence.NamedAttributeNode;
import javax.persistence.NamedEntityGraph;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.PostLoad;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.apache.lucene.analysis.cn.smart.SmartChineseAnalyzer;
import org.hibernate.annotations.Formula;
import org.hibernate.search.annotations.Analyze;
import org.hibernate.search.annotations.Analyzer;
import org.hibernate.search.annotations.Boost;
import org.hibernate.search.annotations.Field;
import org.hibernate.search.annotations.Index;
import org.hibernate.search.annotations.Indexed;
import org.hibernate.search.annotations.IndexedEmbedded;
import org.hibernate.search.annotations.Store;
import org.hibernate.validator.constraints.Length;
import com.ews.onlineexchange.comparator.TradeApplyDateComparator;
import com.ews.onlineexchange.model.assess.CreditLevel;
import com.ews.onlineexchange.util.SystemUUIDUtil;

/**
 * 
 * 公告 说明：serviceBuyer 采购方发布的公告
 * 
 * @author wcq
 *
 */
@Entity
@NamedEntityGraph( name = "Bulletin.QualificationGroup", attributeNodes = {
		@NamedAttributeNode( value = "qualificationGroup" ),
		@NamedAttributeNode( value = "buyer" ),
		@NamedAttributeNode( value = "qualificationTemplate" ),
		@NamedAttributeNode( value = "currRejectRecord" )
	} 
	
)
@Indexed(index = "all")
@Boost(2.0F)
public class Bulletin extends DataEntity implements FullTextResult {
	public static final int CHOOSE_BEST_TRADE_COUNT 				= 3;

	private static final long			serialVersionUID			= 1L;
	private String 						serialNumber;															// 公告编号
	private String						title;																	// 公告标题
	private	ServiceBuyer				buyer;																	// 项目采购单位
	private String						projectName;															// 采购项目名称
	private BigDecimal					amountOfInvestment;														// 项目总投资
	private String						servicePrice;															//服务金额（字符串）
	private BigDecimal					price;																	// 服务金额
	private BigDecimal					startingPrice;															// 起始价
	private String						priceRemark;															// 服务金额说明
	private String						content;																// 服务内容
	private String						projectInfo;															// 项目建设内容
	private QualificationTemplate		qualificationTemplate	;												// 资质要求
	private QualificationGroup          qualificationGroup ;													// 资质要求(新)
	private String						reqRemark;																// 公告要求说明
	private Integer						limitOfTime;															// 合同约定完成时限
	private LimitUnit					limitUnit;																// 时间单位
	private String						limitRemark;															// 合同约定完成时限说明
	private String						wayOfPayment;															// 付款方式
	private ChooseType					wayOfChoose;															// 中介选取方式
	private Date						endingApplyTime;														// 截止报名时间
	private String						addressOfChoose;														// 公开选取中介地址
	private Date						chooseTime;																// 开始选取时间
	private String						teleOfConsult;															// 咨询电话
	private String						teleOfSupervise;														// 监督电话
	private List<Trade>					trades = new ArrayList<>();												// 申请信息
	
	private Boolean						auditResult;															// 是否已通过审核
	private String						auditor;																// 审核人
	private Date						auditTime;																// 审核时间
	
	private boolean 					over;
	private Date						overTime;
	private Integer						overYear;
	private Integer 					overMonth;
	private Trade						successTrade;
	private String 						winnerId;//存储中选中介的id
	private boolean 					cancelled = false;
	private Date						cancelTime;
	private String						cancelReason;
	private User						cancelOperator;
	
//	private int							applyCount = 0 ;
	private boolean						applyDone;									// 是否申请结算
	private Date						applyDoneTime;								// 申请结算时间
	private BigDecimal					applyDonePrice;			
	private boolean 					done;										// 评价完成
	private Date						doneTime;
	
	private TradeStatus                 tradeStatus = TradeStatus.Opened;
	
	private RejectRecord 				currRejectRecord;														//最新一次驳回记录
	private List<RejectRecord>			historyRejectRecords;													//历史驳回记录
		
	private TimeSpan 					timeSPan = TimeSpan.Middle;
	
	/*
	 * 2019-11-09
	 * 司冰
	 * 增加择优备选名单数量字段
	 */
	private Short						candidateTradeCount	= 3;												//候选中介数量（近）
	private List<Trade>					bestTrades = new ArrayList<>();											// 择优选择备审结果，数量参见CHOOSE_BEST_TRADE_COUNT
	private String						bestTradeSupplierNames;
	private List<Image>					photos = 	 new ArrayList<Image>();									// 单位盖章文件照片
//	private ServiceSupplier 			manualSupplier;                                                 		//直接选取中介
	private List<Trade>					backupTrades = new ArrayList<>();
	private Integer 					hasAppliedCount = 0 ; 
	private List<ContractRecord>		contractRecords = new ArrayList<>();
	private List<Attachment>			attachments			= new ArrayList<Attachment>();
	private String						tempSupplierId;
	private String						bulletinReqStr;
	
	/*
	 * 2019-11-12
	 * 司冰
	 * 增加是否报名人数不足时自动作废标志字段
	 */
	private boolean						autoPass;					// 当报名人数不足时自动作废，无候选大于1家，有候选满足候选家数
	
	/*
	 * 2019-11-12
	 * 司冰
	 * 增加互斥型关联公告字段
	 */
	private Bulletin 					 	conflictingBulletin;
	
	
	private List<Attachment>			 	appliedDoneAttachments	= new ArrayList<>();// 采购方结算相关附件
	private List<Attachment>			 	assessAttachments	= new ArrayList<>();// 评审选取相关附件
	private List<Image>					 	assessImages = 	 new ArrayList<Image>();									// 单位盖章文件照片
	private BigDecimal					 	industryStandard;																//竞价和均价选取使用的行业标准	
	private BigDecimal					 	agreedPrice;																	//竞价和均价选取的最终成交价
	private List<RequirementDefination>	 	defs = new ArrayList<>();																	//公告要求上传附件
	private String							reChooseReason;							//重新选取原因或作废原因
	
	private Date							buyerLastUpdateTime;
	
	private boolean							published;
	
	@NotNull
	@Column(unique = true)
	public String getSerialNumber() {
		return serialNumber;
	}

	public void setSerialNumber(String serialNumber) {
		this.serialNumber = serialNumber;
	}

	
	@OneToMany( mappedBy="bulletin" )	
	public List<ContractRecord> getContractRecords() {
		return contractRecords;
	}

	public void setContractRecords( List<ContractRecord> contractRecords ) {
		this.contractRecords = contractRecords;
	}

	@NotNull
	@Length(min = 1, max = 500, message = "公告标题长度不能超过500")
	@Field(analyzer = @Analyzer(impl = SmartChineseAnalyzer.class))
	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}	
	
	@ManyToOne
	@NotNull
	@IndexedEmbedded	
	public ServiceBuyer getBuyer() {
		return buyer;
	}

	public void setBuyer(ServiceBuyer buyer) {
		this.buyer = buyer;
	}
	
	
//	@OneToOne
//	public ServiceSupplier getManualSupplier() {
//		return manualSupplier;
//	}
//
//	public void setManualSupplier(ServiceSupplier manualSupplier) {
//		this.manualSupplier = manualSupplier;
//	}

	@Formula( value = " ( SELECT COUNT(t.id) FROM Trade t WHERE t.apply = 1  AND t.bulletin_id = id ) " )//AND t.hasVerify = 1
	public Integer getHasAppliedCount() {
		return hasAppliedCount;
	}

	public void setHasAppliedCount(Integer hasAppliedCount) {
		this.hasAppliedCount = hasAppliedCount;
	}

	@NotNull
	@Length(min = 1, max = 100, message = "采购项目名称长度不能超过100")
	@Field(analyzer = @Analyzer(impl = SmartChineseAnalyzer.class))
	@Boost(2F)
	public String getProjectName() {
		return projectName;
	}

	public void setProjectName(String projectName) {
		this.projectName = projectName;
	}

	@NotNull
	@Min(0L)
	public BigDecimal getAmountOfInvestment() {
		return amountOfInvestment;
	}

	public void setAmountOfInvestment(BigDecimal amountOfInvestment) {
		this.amountOfInvestment = amountOfInvestment;
	}
	
	public String getServicePrice() {
		return servicePrice;
	}

	public void setServicePrice(String servicePrice) {
		this.servicePrice = servicePrice;
	}

	public BigDecimal getPrice() {
		return price;
	}

	public void setPrice(BigDecimal price) {
		this.price = price;
	}
	
	
	
	public BigDecimal getApplyDonePrice() {
		return applyDonePrice;
	}

	public void setApplyDonePrice(BigDecimal applyDonePrice) {
		this.applyDonePrice = applyDonePrice;
	}

	public String getPriceRemark() {
		return priceRemark;
	}

	public void setPriceRemark(String priceRemark) {
		this.priceRemark = priceRemark;
	}
	
	
	@OneToMany(cascade = CascadeType.ALL,fetch = FetchType.LAZY)
	public List<RequirementDefination> getDefs() {
		return defs;
	}

	public void setDefs(List<RequirementDefination> defs) {
		this.defs = defs;
	}

	@NotNull
	@Length(max = 1000)	
	@Field(analyzer = @Analyzer(impl = SmartChineseAnalyzer.class))
	public String getContent() {
		return content;
	}

	public void setContent(String content) {
		this.content = content;
	}
	@OneToMany(fetch = FetchType.LAZY)
	@JoinTable(name="BULLETIN_BACKUPTRADES")
	public List<Trade> getBackupTrades() {
		return backupTrades;
	}

	public void setBackupTrades(List<Trade> backupTrades) {
		this.backupTrades = backupTrades;
	}

	@NotNull
	@Length(max = 1000)
	@Field(analyzer = @Analyzer(impl = SmartChineseAnalyzer.class))
	public String getProjectInfo() {
		return projectInfo;
	}

	public void setProjectInfo(String projectInfo) {
		this.projectInfo = projectInfo;
	}
	
	@Length(max = 1000)	
	public String getReqRemark() {
		return reqRemark;
	}

	public void setReqRemark(String reqRemark) {
		this.reqRemark = reqRemark;
	}

	@NotNull
	public Integer getLimitOfTime() {
		return limitOfTime;
	}

	public void setLimitOfTime(Integer limitOfTime) {
		this.limitOfTime = limitOfTime;
	}

	@Enumerated(EnumType.STRING)
	@NotNull
	public LimitUnit getLimitUnit() {
		return limitUnit;
	}

	public void setLimitUnit(LimitUnit limitUnit) {
		this.limitUnit = limitUnit;
	}

	@Length(max = 2000)	
	public String getLimitRemark() {
		return limitRemark;
	}

	public void setLimitRemark(String limitRemark) {
		this.limitRemark = limitRemark;
	}

	@NotNull
	@Field(index = Index.YES, analyze = Analyze.YES, store = Store.NO)
	public String getWayOfPayment() {
		return wayOfPayment;
	}

	public void setWayOfPayment(String wayOfPayment) {
		this.wayOfPayment = wayOfPayment;
	}
	
	@NotNull
	@Enumerated(EnumType.STRING)
	public ChooseType getWayOfChoose() {
		return wayOfChoose;
	}

	public void setWayOfChoose(ChooseType wayOfChoose) {
		this.wayOfChoose = wayOfChoose;
	}

	@Temporal(TemporalType.TIMESTAMP)
	public Date getEndingApplyTime() {
		return endingApplyTime;
	}

	public void setEndingApplyTime(Date endingApplyTime) {
		this.endingApplyTime = endingApplyTime;
	}

	@NotNull
	public String getAddressOfChoose() {
		return addressOfChoose;
	}

	public void setAddressOfChoose(String addressOfChoose) {
		this.addressOfChoose = addressOfChoose;
	}

	@Temporal(TemporalType.TIMESTAMP)
	public Date getChooseTime() {
		return chooseTime;
	}

	public void setChooseTime(Date chooseTime) {
		this.chooseTime = chooseTime;
	}

	@NotNull
	public String getTeleOfConsult() {
		return teleOfConsult;
	}

	public void setTeleOfConsult(String teleOfConsult) {
		this.teleOfConsult = teleOfConsult;
	}

	@NotNull
	public String getTeleOfSupervise() {
		return teleOfSupervise;
	}

	public void setTeleOfSupervise(String teleOfSupervise) {
		this.teleOfSupervise = teleOfSupervise;
	}
	
	@OneToMany(mappedBy = "bulletin",fetch = FetchType.LAZY)
	public List<Trade> getTrades() {
		return trades;
	}

	public void setTrades(List<Trade> trades) {
		this.trades = trades;
	}

	@Field(analyze = Analyze.NO)
	public Boolean getAuditResult() {
		return auditResult;
	}

	public void setAuditResult(Boolean auditResult) {
		this.auditResult = auditResult;
	}

	public String getAuditor() {
		return auditor;
	}

	public void setAuditor(String auditor) {
		this.auditor = auditor;
	}

	@Temporal(TemporalType.TIMESTAMP)
	public Date getAuditTime() {
		return auditTime;
	}

	public void setAuditTime(Date auditTime) {
		this.auditTime = auditTime;
	}

	public boolean isOver() {
		return over;
	}

	public void setOver(boolean over) {
		this.over = over;
	}

	@Temporal(TemporalType.TIMESTAMP)
	public Date getOverTime() {
		return overTime;
	}

	public void setOverTime(Date overTime) {
		this.overTime = overTime;
	}

	public Integer getOverYear() {
		return overYear;
	}

	public void setOverYear(Integer overYear) {
		this.overYear = overYear;
	}

	public Integer getOverMonth() {
		return overMonth;
	}

	public void setOverMonth(Integer overMonth) {
		this.overMonth = overMonth;
	}

	@OneToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE } )
	public Trade getSuccessTrade() {
		return successTrade;
	}
	@OneToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE })
	public QualificationTemplate getQualificationTemplate() {
		return qualificationTemplate;
	}

	public void setQualificationTemplate(QualificationTemplate qualificationTemplate) {
		this.qualificationTemplate = qualificationTemplate;
	}

	public void setSuccessTrade(Trade successTrade) {
		this.successTrade = successTrade;
	}

	public BigDecimal getStartingPrice() {
		return startingPrice;
	}

	public void setStartingPrice(BigDecimal startingPrice) {
		this.startingPrice = startingPrice;
	}

	public boolean isCancelled() {
		return cancelled;
	}

	public void setCancelled(boolean cancelled) {
		this.cancelled = cancelled;
	}

	@Temporal(TemporalType.TIMESTAMP)
	public Date getCancelTime() {
		return cancelTime;
	}

	public void setCancelTime(Date cancelTime) {
		this.cancelTime = cancelTime;
	}
	
	@Size(max = 4000)
	public String getCancelReason() {
		return cancelReason;
	}

	public void setCancelReason(String cancelReason) {
		this.cancelReason = cancelReason;
	}
	@OneToOne
	public User getCancelOperator() {
		return cancelOperator;
	}

	public void setCancelOperator(User cancelOperator) {
		this.cancelOperator = cancelOperator;
	}
	
	public boolean isDone() {
		return done;
	}

	public void setDone(boolean done) {
		this.done = done;
	}
		
	public BigDecimal getAgreedPrice() {
		return agreedPrice;
	}

	public void setAgreedPrice(BigDecimal agreedPrice) {
		this.agreedPrice = agreedPrice;
	}

	public BigDecimal getIndustryStandard() {
		return industryStandard;
	}

	public void setIndustryStandard(BigDecimal industryStandard) {
		this.industryStandard = industryStandard;
	}

	@Temporal(TemporalType.TIMESTAMP)
	public Date getDoneTime() {
		return doneTime;
	}

	public void setDoneTime(Date doneTime) {
		this.doneTime = doneTime;
	}

	@Enumerated(EnumType.STRING)
	public TradeStatus getTradeStatus() {
		return tradeStatus;
	}

	public void setTradeStatus(TradeStatus tradeStatus) {
		this.tradeStatus = tradeStatus;
	}
	
	@Enumerated(EnumType.STRING)
	public TimeSpan getTimeSPan() {
		return timeSPan;
	}

	public void setTimeSPan(TimeSpan timeSPan) {
		this.timeSPan = timeSPan;
	}

	@OneToOne(cascade = CascadeType.ALL)
	public RejectRecord getCurrRejectRecord() {
		return currRejectRecord;
	}

	public void setCurrRejectRecord(RejectRecord currRejectRecord) {
		this.currRejectRecord = currRejectRecord;
	}

	@OneToMany(fetch = FetchType.LAZY)
	public List<RejectRecord> getHistoryRejectRecords() {
		return historyRejectRecords;
	}

	public void setHistoryRejectRecords(List<RejectRecord> historyRejectRecords) {
		this.historyRejectRecords = historyRejectRecords;
	}

	
	@Min( 1 ) @Max( 5 )
	public Short getCandidateTradeCount() {
		return candidateTradeCount;
	}

	public void setCandidateTradeCount(Short candidateTradeCount) {
		this.candidateTradeCount = candidateTradeCount;
	}

	@OneToMany(fetch = FetchType.LAZY)
	public List<Trade> getBestTrades() {
		return this.bestTrades;
	}

	public void setBestTrades(List<Trade> bestTrades) {
		this.bestTrades = bestTrades;
	}
	
//	@Formula( value = "	(	SELECT 		 	listagg ( su.cnname,',' ) within group(order by su.id )  FROM			System_User su, ServiceSupplier ss, Trade t, Bulletin_Trade bt  WHERE			su.id = ss.id 	AND				ss.id = t.supplier_id 		AND				t.id = bt.besttrades_id 		AND				bt.bulletin_id = id	  ) ") 
//	@Transient
	@Length( max = 1000)
	public String getBestTradeSupplierNames() {
		return bestTradeSupplierNames;
	}

	public void setBestTradeSupplierNames( String bestTradeSupplierNames ) {
		this.bestTradeSupplierNames = bestTradeSupplierNames;
	}

	public boolean isApplyDone() {
		return applyDone;
	}

	public void setApplyDone(boolean applyDone) {
		this.applyDone = applyDone;
	}

	@Temporal(TemporalType.TIMESTAMP)
	public Date getApplyDoneTime() {
		return applyDoneTime;
	}

	public void setApplyDoneTime(Date applyDoneTime) {
		this.applyDoneTime = applyDoneTime;
	}
	
	@Lob
	public String getBulletinReqStr() {
		return bulletinReqStr;
	}

	public void setBulletinReqStr(String bulletinReqStr) {
		this.bulletinReqStr = bulletinReqStr;
	}

	//	@Size(min = 1)
	@OneToMany(cascade = CascadeType.ALL,fetch = FetchType.LAZY)
	public List<Image> getPhotos() {
		return photos;
	}

	public void setPhotos(List<Image> photos) {
		this.photos = photos;
	}
	
	@JoinTable(name = "BULLEIN_ASSESSIMAGE")
	@OneToMany(cascade = CascadeType.ALL,fetch = FetchType.LAZY)
	public List<Image> getAssessImages() {
		return assessImages;
	}

	public void setAssessImages(List<Image> assessImages) {
		this.assessImages = assessImages;
	}

	@JoinTable(name ="BULLETIN_APPLIEDATTCHMENTS")
	@OneToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE},fetch = FetchType.LAZY)
	public List<Attachment> getAppliedDoneAttachments() {
		return appliedDoneAttachments;
	}

	public void setAppliedDoneAttachments(List<Attachment> appliedDoneAttachments) {
		this.appliedDoneAttachments = appliedDoneAttachments;
	}

	
	@JoinTable(name ="BULLETIN_ASSESSATTCHMENTS")
	@OneToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE},fetch = FetchType.LAZY)
	public List<Attachment> getAssessAttachments() {
		return assessAttachments;
	}

	public void setAssessAttachments(List<Attachment> assessAttachments) {
		this.assessAttachments = assessAttachments;
	}

	@OneToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE},fetch = FetchType.LAZY)
	public List<Attachment> getAttachments() {
		return attachments;
	}

	public void setAttachments(List<Attachment> attachments) {
		this.attachments = attachments;
	}
	
	@Transient
	public List<Trade> getVerifiedTrades() {
		List<Trade> resultList = new ArrayList<>();
		
		for (Trade trade : getTrades()) {
			if ( trade.isHasVerify() && trade.isApply() )
				resultList.add(trade);
		}
		Collections.sort(resultList, new TradeApplyDateComparator());
		return resultList;
	}

	@Transient
	public List<Trade> getPricedTrades() {
		List<Trade> resultList = new ArrayList<>();
		
		for (Trade trade : getVerifiedTrades()) {
			if ( trade.getOffer() != null )//&& trade.getOffer().compareTo( new BigDecimal( 0 ) ) > 0
				resultList.add(trade);
		}
		Collections.sort(resultList, new TradeApplyDateComparator());
		return resultList;
	}
	
	@Transient
	public BigDecimal getAverage() {
		if ( !this.getWayOfChoose().equals( ChooseType.Average ) ) { 
			throw new RuntimeException( "Not an average bulletin !!!" ); 
		}
		
		List<Trade> pricedTrades = getPricedTrades();
		int count = pricedTrades.size();	
		BigDecimal average = new BigDecimal( 0 );
		for ( Trade trade : pricedTrades ) {
			average = average.add( trade.getOffer() );
		}
		
		if ( count > 0 )
			average = average.divide( new BigDecimal( count ), BigDecimal.ROUND_HALF_UP );
		return average;
	}
	
	@Transient
	public List<Trade> getNotVerifiedTrades() {
		List<Trade> resultList = new ArrayList<>();
		for (Trade trade : getTrades()) {
			if (!trade.isHasVerify() && !trade.isApply())
				resultList.add(trade);
		}
		return resultList;
	}


	@Transient
	public BigDecimal getAverageOffer() {
		List<Trade> verifiedTrades = getVerifiedTrades();
		BigDecimal average = new BigDecimal(0);		
		for (Trade trade : verifiedTrades) {
			average = average.add(trade.getOffer());
		}
		return average.divide(new BigDecimal(verifiedTrades.size()), 2, RoundingMode.UNNECESSARY);
	}
	
	@OneToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE })
//	@NotNullec
	public QualificationGroup getQualificationGroup() {
		return qualificationGroup;
	}

	public void setQualificationGroup(QualificationGroup qualificationGroup) {
		this.qualificationGroup = qualificationGroup;
	}
	
	public boolean isAutoPass() {
		return autoPass;
	}

	public void setAutoPass( boolean autoPass ) {
		this.autoPass = autoPass;
	}
	
	@ManyToOne
	public Bulletin getConflictingBulletin() {
		return conflictingBulletin;
	}
	
	@Transient
	public Bulletin getRootConflictingBulletin() {
		if ( this.getConflictingBulletin() == null )
			return null;
		
		if ( this.getConflictingBulletin().getConflictingBulletin() == null ) {
			return this.getConflictingBulletin();
		} else {
			return this.getConflictingBulletin().getConflictingBulletin();
		}
	}
	
	public void setConflictingBulletin(Bulletin conflictingBulletin) {
		this.conflictingBulletin = conflictingBulletin;
	}
		
	public String getWinnerId() {
		return winnerId;
	}

	public void setWinnerId(String winnerId) {
		this.winnerId = winnerId;
	}
	
	public String getReChooseReason() {
		return reChooseReason;
	}

	public void setReChooseReason(String reChooseReason) {
		this.reChooseReason = reChooseReason;
	}

	@Transient
	public boolean alreadyApplyOver() {
		if(null != getChooseTime() && getChooseTime().before(new Date())) {
			return true;
		}
		return false;
	}


	public String getTempSupplierId() {
		return tempSupplierId;
	}

	public void setTempSupplierId(String tempSupplierId) {
		this.tempSupplierId = tempSupplierId;
	}

	@Temporal(TemporalType.TIMESTAMP)
	public Date getBuyerLastUpdateTime() {
		return buyerLastUpdateTime;
	}
	
	
	public void setBuyerLastUpdateTime(Date buyerLastUpdateTime) {
		this.buyerLastUpdateTime = buyerLastUpdateTime;
	}

	public boolean isPublished() {
		return published;
	}

	public void setPublished( boolean published ) {
		this.published = published;
	}

	@Transient
	public boolean isChoosable() {
		if (this.isOver())
			return false;
		if (this.getAuditResult() == null || !this.getAuditResult())
			return false;
		if (!this.getTradeStatus().equals(TradeStatus.Opened))
			return false;
		if (this.getEndingApplyTime().after(new Date()))
			return false;
		if (this.getChooseTime().before(new Date()))
			return false;
		if (!this.getWayOfChoose().equals(ChooseType.Random) && !this.getWayOfChoose().equals(ChooseType.Best) && !this.getWayOfChoose().equals(ChooseType.Price) && !this.getWayOfChoose().equals(ChooseType.Average))
			return false;
		if(this.getHasAppliedCount()  == 0) 
			return false;
		return true;
	}
	
	@PostLoad
	protected void postLoad() {
		this.setHighLightTitle(this.getProjectName());
		StringBuilder sb = new StringBuilder();
		sb	.append(this.getBuyer().getCnname())
			.append(",")
			.append(this.getTitle())
			.append(",")
			.append(this.getProjectName())
		;
			
		this.setHighLightContent(sb.toString());
		
		if(qualificationGroup !=null) {
			List<QualificationGroup> serviceGroups = qualificationGroup.getAgentServiceGroups();
			if(serviceGroups.size() > 0) {
				for (QualificationGroup qualificationGroup  : serviceGroups) {
					qualificationGroup.setGroupIndex(SystemUUIDUtil.getUUID());
					
				}
				
			}
			List<QualificationGroup> qualificationGroups = qualificationGroup.getQualificationGroups();
			if(qualificationGroups.size() > 0) {
				for (QualificationGroup qualificationGroup  : qualificationGroups) {
					qualificationGroup.setGroupIndex(SystemUUIDUtil.getUUID());
					
				}
			}
		}
		
	}
	
	@Transient
	public String bestChooseSupplier() {
		StringBuilder sb = new StringBuilder();
			for(Trade trade : getBestTrades()) {
				sb.append(trade.getSupplier().getCnname());
				sb.append("、");
			}
		String str = sb.toString();
		str = str.substring(0,str.length()-1);
		return str;
	}
	
	@PrePersist
	@PreUpdate
	private void preSave() {
		this.setTitle("关于为"+this.getProjectName()+""+this.getWayOfChoose().getTypeName() + "中介机构的公告");
		
//		this.setConflictingBulletin( this.getRootConflictingBulletin() );
		
		if ( this.getWayOfChoose().equals( ChooseType.Average ) || this.getWayOfChoose().equals( ChooseType.Price ) ) {
			this.setTimeSPan( TimeSpan.Short );
		}
	}
	
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = super.hashCode();
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		result = prime * result + ((projectName == null) ? 0 : projectName.hashCode());
		result = prime * result + ((title == null) ? 0 : title.hashCode());
		return result;
	}


	
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (!super.equals(obj))
			return false;
		if (getClass() != obj.getClass())
			return false;
		Bulletin other = (Bulletin) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		
		if (projectName == null) {
			if (other.projectName != null)
				return false;
		} else if (!projectName.equals(other.projectName))
			return false;
		if (title == null) {
			if (other.title != null)
				return false;
		} else if (!title.equals(other.title))
			return false;
		return true;
	}

	@Transient
	public boolean ratable() {
		if (!this.getTradeStatus().equals(TradeStatus.Agreed))
			return false;
		if (this.isDone())
			return false;
		if (this.getSuccessTrade() == null)
			return false;
		if (!this.isApplyDone())
			return false;
		if (this.getSuccessTrade().getTradeRating() != null)
			return false;
		return true;
	}
	/**
	 * 评价后，管理员修改恶意差评
	 * @return
	 */
	@Transient
	public boolean reratable() {
		if (!this.getTradeStatus().equals(TradeStatus.Agreed))
			return false;
//		if (this.isDone())
//			return false;
		if (this.getSuccessTrade() == null)
			return false;
		if (!this.isApplyDone())
			return false;
		if (this.getSuccessTrade().getTradeRating() != null)
			return false;
		return true;
	}
	
	@Transient
	public boolean isManualChoosable() {
		if (!getWayOfChoose().equals(ChooseType.Manual))
			return false;
		if (this.isOver())
			return false;
		if (!this.getTradeStatus().equals(TradeStatus.Opened))
			return false;
		if (this.getAuditResult() == null || !this.getAuditResult())
			return false;
		if (this.getEndingApplyTime().after(new Date()))
			return false;
		if (this.getChooseTime().after(new Date()))
			return false;
		return true;
	}
	
	@Transient
	public void addBulletinQualificationRequirement() {
		addEmptyAllMatchGroup();
	}
	public void addBulletinAgentServiceRequirement() {
		addEmptyAllMatchAgentServiceGroup();
	}
	
	@Transient
	public void addEmptyAllMatchAgentServiceGroup() {
		if(null == getQualificationGroup()) {
			setQualificationGroup(new QualificationGroup());
		}
		QualificationGroup newGroup = new QualificationGroup();
		newGroup.setBulletin(this);
		newGroup.setRelation(Relation.And);
		newGroup.setGroupIndex(SystemUUIDUtil.getUUID());
		AgentService service = new AgentService();
		service.setServiceIndex(SystemUUIDUtil.getUUID());
		newGroup.getAgentServices().add(service);
		newGroup.setParentGroup(getQualificationGroup());
		newGroup.setRequireType(RequireType.AgentServiceRequirement);
		getQualificationGroup().getAgentServiceGroups().add(newGroup);
	}
	
	@Transient
	public void addEmptyPartMatchAgentServiceGroup() {
		if(null == getQualificationGroup()) {
			setQualificationGroup(new QualificationGroup());
		}
		QualificationGroup newGroup = new QualificationGroup();
		newGroup.setBulletin(this);
		newGroup.setRelation(Relation.Or);
		
		newGroup.setGroupIndex(SystemUUIDUtil.getUUID());
		AgentService service = new AgentService();
		service.setServiceIndex(SystemUUIDUtil.getUUID());
		newGroup.getAgentServices().add(service);
		newGroup.setParentGroup(getQualificationGroup());
		newGroup.setRequireType(RequireType.AgentServiceRequirement);
		getQualificationGroup().getAgentServiceGroups().add(newGroup);		
	}
	

	
	@Transient
	public void addEmptyAllMatchGroup() {
		if(null == getQualificationGroup()) {
			setQualificationGroup(new QualificationGroup());
		}
		QualificationGroup newGroup = new QualificationGroup();
		newGroup.setBulletin(this);
		newGroup.setGroupIndex(SystemUUIDUtil.getUUID());
		newGroup.setRelation(Relation.And);
		QualificationTemplate template = new QualificationTemplate();
		template.setTemplateIndex(SystemUUIDUtil.getUUID());
		newGroup.getQualificationTemplates().add(template);
		newGroup.setParentGroup(getQualificationGroup());
		newGroup.setRequireType(RequireType.QualificationRequirement);
		getQualificationGroup().getQualificationGroups().add(newGroup);
	}
	
	@Transient
	public void addEmptyPartMatchGroup() {
		if(null == getQualificationGroup()) {
			setQualificationGroup(new QualificationGroup());
		}
		QualificationGroup newGroup = new QualificationGroup();
		newGroup.setBulletin(this);
		newGroup.setGroupIndex(SystemUUIDUtil.getUUID());
		newGroup.setRelation(Relation.Or);
		QualificationTemplate template = new QualificationTemplate();
		template.setTemplateIndex(SystemUUIDUtil.getUUID());
		newGroup.getQualificationTemplates().add(template);
		newGroup.setParentGroup(getQualificationGroup());
		newGroup.setRequireType(RequireType.QualificationRequirement);
		getQualificationGroup().getQualificationGroups().add(newGroup);		
	}
	@Transient
	public String getBackUpSupplierNames() {
		StringBuilder sb = new StringBuilder();
		for (Trade trade : backupTrades) {
			sb.append(trade.getSupplier().getCnname());
			sb.append("、");
		}
		
		if (sb.length() == 0)
			return "";
		return sb.substring(0, sb.length() - 1); 
	}
	
	@Transient
	private List<Trade> getSlotTrades(){
		if(wayOfChoose.equals(ChooseType.Best))
			return getBestTrades();
		else {
			List<Trade> randomTrades = getBackupTrades();
			randomTrades.add(0, successTrade);
			return randomTrades;
		}		
	}

	@Transient
	public int getLeftDays() {
		if (getChooseTime().before(new Date()))
			return 0;
		return Math.abs((int)((getChooseTime().getTime() - new Date().getTime()) / (1000 * 60 * 60 * 24)));
	}
	
	@Transient
	public int getLeftHours() {
		if (getChooseTime().before(new Date()))
			return 0;
		long hourMillis = getChooseTime().getTime() - new Date().getTime();
		return Math.abs((int)((hourMillis % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)));
	}
	
	
	
	@Transient
	public int getLeftMinutes() {
		if (getChooseTime().before(new Date()))
			return 0;
		long minuteMillis = getChooseTime().getTime() - new Date().getTime();
		return Math.abs((int)((minuteMillis % (1000 * 60 * 60)) / (1000 * 60)));
	}
	@Transient
	public int getLeftSeconds() {
		if (getChooseTime().before(new Date()))
			return 0;
		long secondMillis = getChooseTime().getTime() - new Date().getTime();
		return Math.abs((int)((secondMillis % (1000 * 60)) / 1000));
	}
//	@Inject
//	EntityManager entityManager;

	boolean createContractable;
	
	@Formula( " CASE WHEN ( SELECT COUNT(cr.id) FROM contractRecord cr WHERE cr.bulletin_id = id ) > 0 AND tradeStatus = 'Agreed' AND overtime + 1 < SYSDATE THEN 0 ELSE 1 END " ) 
	public boolean isCreateContractable() {
		return createContractable;
	}

	public void setCreateContractable( boolean createContractable ) {
		this.createContractable = createContractable;
	}
	
	boolean hasZF;
	
	@Formula( " CASE WHEN ( SELECT COUNT(cr.id) FROM BulletinDiscardRecord cr WHERE cr.bulletinId = id ) > 0  THEN 1 ELSE 0 END " ) 
	
	public boolean isHasZF() {
		return hasZF;
	}

	public void setHasZF(boolean hasZF) {
		this.hasZF = hasZF;
	}

	@Transient
	public boolean isShowStartBtn() {
		if(null != getChooseTime() && getChooseTime().after(new Date()) && null != getEndingApplyTime() && getEndingApplyTime().before(new Date())) {
			
			return true;
		}
		return false;
	}

	@Transient
	public boolean isShowCancelBtn() {
		if(null != getChooseTime() && getChooseTime().after(new Date()) && null != getEndingApplyTime() && getEndingApplyTime().before(new Date())) {
	
			return true;
		}
		return false;
	}
	
	
	/* 获取结果公示的日期，成交后一天*/
	@Transient
	public Date getAgreedBulletinDate() {
		Date overDate = getOverTime();
		if(overDate == null) return null;
		Calendar cal = Calendar.getInstance();
		cal.setTime(overDate);
		cal.set(Calendar.DAY_OF_MONTH,cal.get(Calendar.DAY_OF_MONTH)+1);
		Date newDate = cal.getTime();
		return newDate;
	}
	@Transient
	public String getAgreedBulletinTitle() {
		StringBuilder sb = new StringBuilder();
		sb.append("关于为【");
		sb.append(this.getProjectName());
		sb.append("】");
		sb.append("选取结果的公告");
		return sb.toString();
	}
	
	@Transient
	public String getShowingBulletinTitle() {
		StringBuilder sb = new StringBuilder();
		sb.append("关于为【");
		sb.append(this.getProjectName());
		sb.append("】");
		sb.append("中选结果的公示");
		return sb.toString();
	}
	
	
	
	@Transient
	public boolean isNotEnoughApply() {
		if ( getWayOfChoose().equals( ChooseType.Best ) ) {
			return this.getVerifiedTrades().size() >= this.getCandidateTradeCount();
		} else {
			return this.getVerifiedTrades().size() > 1;
		}
	}
	
	@Transient
	public String getConflictingSupplierId() {
		if ( this.getConflictingBulletin() == null )
			return "EMPTY";
		if ( this.getConflictingBulletin().getSuccessTrade() == null )
			return "EMPTY";
		return this.getConflictingBulletin().getSuccessTrade().getSupplier().getId();
	}
	
	@Transient
	public BigDecimal getCreditScoreLimit() {
		//2020-08-31  修改  将折优和评审的等级限制调整为C

		if ( this.getWayOfChoose().equals( ChooseType.Random ) ) {
			return new BigDecimal( CreditLevel.C.getMin() );
		} else if ( this.getWayOfChoose().equals( ChooseType.Best ) ) {
			return new BigDecimal( CreditLevel.B.getMin() );
		} else if ( this.getWayOfChoose().equals( ChooseType.Manual ) ) {
			return new BigDecimal( CreditLevel.A.getMin() );
		} else if ( this.getWayOfChoose().equals( ChooseType.Price ) ) {
			return new BigDecimal( CreditLevel.C.getMin() );
		} else if ( this.getWayOfChoose().equals( ChooseType.Average ) ) {
			return new BigDecimal( CreditLevel.C.getMin() );
		}
		
		throw new RuntimeException();
	}

	
}
